use serde::{Deserialize, Serialize};

#[derive(Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
pub enum MessageType {
    #[default]
    EMPTY,
    RESPONSE,
    REQUEST,
    ERROR,
}

#[derive(Debug, Default, Clone, Deserialize, Serialize, Eq, PartialEq)]
pub enum MessageCommand {
    #[default]
    PING,
    HTTP,
    CLI,
    BATCHSCRIPT,
    PUBSUB,
    UICOMMAND,
    NONE,
}

#[derive(Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct Message {
    pub request_id: u64,
    pub r#type: MessageType,
    pub command: MessageCommand,
    pub data: Vec<u8>,
}

impl Message {
    pub fn error() -> Message {
        Self {
            request_id: 0,
            r#type: MessageType::ERROR,
            command: MessageCommand::NONE,
            data: vec![],
        }
    }
    pub fn error_with_code(code: u64) -> Message {
        Self {
            request_id: code,
            r#type: MessageType::ERROR,
            command: MessageCommand::NONE,
            data: vec![],
        }
    }

    pub fn error_with_code_message(
        code: u64,
        error_message: &str,
        command: MessageCommand,
    ) -> Message {
        Self {
            request_id: code,
            r#type: MessageType::ERROR,
            command: command,
            data: String::from(error_message).as_bytes().to_vec(),
        }
    }

    pub fn error_with_request_id(
        request_id: u64,
        error_message: &str,
        command: MessageCommand,
    ) -> Message {
        Self {
            request_id: request_id,
            r#type: MessageType::ERROR,
            command: command,
            data: String::from(error_message).as_bytes().to_vec(),
        }
    }

    pub fn timeout() -> Message {
        Self {
            request_id: 408,
            r#type: MessageType::ERROR,
            command: MessageCommand::NONE,
            data: String::from("Timeout").as_bytes().to_vec(),
        }
    }

    pub fn request(data: Vec<u8>) -> Message {
        Self {
            request_id: 0,
            r#type: MessageType::REQUEST,
            command: MessageCommand::HTTP,
            data: data,
        }
    }
    pub fn response(data: Vec<u8>) -> Message {
        Self {
            request_id: 0,
            r#type: MessageType::RESPONSE,
            command: MessageCommand::HTTP,
            data: data,
        }
    }

    pub fn uicmd(data: Vec<u8>) -> Message {
        Self {
            request_id: 0,
            r#type: MessageType::REQUEST,
            command: MessageCommand::UICOMMAND,
            data: data,
        }
    }

    pub fn uicmd_response(data: Vec<u8>) -> Message {
        Self {
            request_id: 0,
            r#type: MessageType::RESPONSE,
            command: MessageCommand::UICOMMAND,
            data: data,
        }
    }

    pub fn pubsub(data: Vec<u8>) -> Message {
        Self {
            request_id: 0,
            r#type: MessageType::REQUEST,
            command: MessageCommand::PUBSUB,
            data: data,
        }
    }

    pub fn ping() -> Message {
        Self {
            request_id: 0,
            r#type: MessageType::REQUEST,
            command: MessageCommand::PING,
            data: vec![],
        }
    }

    pub fn pone(ping_request_id: u64) -> Message {
        Self {
            request_id: ping_request_id,
            r#type: MessageType::RESPONSE,
            command: MessageCommand::PING,
            data: vec![],
        }
    }

    pub fn cli_request_msg(data: Vec<u8>) -> Message {
        Self {
            request_id: 0,
            r#type: MessageType::REQUEST,
            command: MessageCommand::CLI,
            data: data,
        }
    }

    pub fn batch_script_request_msg(data: Vec<u8>) -> Message {
        Self {
            request_id: 0,
            r#type: MessageType::REQUEST,
            command: MessageCommand::BATCHSCRIPT,
            data: data,
        }
    }

    pub fn cli_response_msg(data: Vec<u8>) -> Message {
        Self {
            request_id: 0,
            r#type: MessageType::RESPONSE,
            command: MessageCommand::CLI,
            data: data,
        }
    }

    pub fn batch_script_response_msg(data: Vec<u8>) -> Message {
        Self {
            request_id: 0,
            r#type: MessageType::RESPONSE,
            command: MessageCommand::BATCHSCRIPT,
            data: data,
        }
    }
}

impl Default for Message {
    fn default() -> Self {
        Self {
            request_id: 0,
            r#type: MessageType::REQUEST,
            command: MessageCommand::PING,
            data: vec![],
        }
    }
}

impl TryFrom<Vec<u8>> for Message {
    type Error = anyhow::Error;

    fn try_from(data: Vec<u8>) -> Result<Self, Self::Error> {
        bincode::deserialize::<Message>(&data[..]).map_err(|err| anyhow::anyhow!("{}", err))
    }
}

impl TryInto<Vec<u8>> for Message {
    type Error = anyhow::Error;

    fn try_into(self) -> Result<Vec<u8>, Self::Error> {
        Ok(bincode::serialize(&self)
            .map_err(|err| anyhow::anyhow!("{}", err))?
            .to_vec())
    }
}

impl std::fmt::Display for Message {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        let mut is_text = true;
        match self.r#type {
            MessageType::ERROR => is_text = true,
            _ => match self.command {
                MessageCommand::HTTP => is_text = true,
                _ => {}
            },
        }
        if is_text {
            write!(
                f,
                "request_id:{},type:{:?},command:{:?},data:{:?}",
                self.request_id,
                self.r#type,
                self.command,
                String::from_utf8(self.data.clone())
            )
        } else {
            write!(
                f,
                "request_id:{},type:{:?},command:{:?},data length:{}",
                self.request_id,
                self.r#type,
                self.command,
                self.data.len()
            )
        }
    }
}

// impl Into<Vec<u8>> for Message {
//     fn into(self) -> Vec<u8> {
//         // self.data
//         bincode::serialize(&self).unwrap().to_vec()
//     }
// }

// impl From<Vec<u8>> for Message {
//     fn from(data: Vec<u8>) -> Self {
//         let o: Option<Message> = bincode::deserialize(&data[..]).unwrap();
//         o.unwrap()
//     }
// }
unsafe impl std::marker::Send for Message {}
